use serde::Serialize;
use tokio::{io::{AsyncRead, AsyncReadExt}, time::timeout};
use std::{str::from_utf8_unchecked, time::Duration};

use crate::{error::{AppErr, static_err, ErrorHandler}, utils::new_bytes};

const READ_DURATION: Duration = Duration::from_millis(500);


#[derive(Debug, Serialize)]
pub struct CtrlProto {
    pub cnt: u16,
    pub cmd_code: u32,
    pub data_len: u32,
    pub data: String,
}

pub fn build_proto(cnt: u16, cmd_code: u32, data: &str) -> String {
    format!("/f/bIII{}III{}III{}III{}III/b/f", cnt, cmd_code, data.len(), data)
}

pub fn build_req(cnt: u16, cmd_code: u32, func: &str, args: &[String]) -> String {
    let data = format!("{}({})", func, args.join(","));
    build_proto(cnt, cmd_code, &data)
}

pub fn is_read_complete(buf: &[u8]) -> bool {
    let len = buf.len();
    if len < 4 {
        return false;
    }
    (buf[len - 4] == b'/') &&
    (buf[len - 3] == b'b') &&
    (buf[len - 2] == b'/') &&
    (buf[len - 1] == b'f')
}

pub async fn read_resp<R: AsyncRead + Unpin>(r: &mut R) -> Result<CtrlProto, AppErr> {

    let mut buf = new_bytes(128);
    let mut resp_str = String::new();

    while !is_read_complete(resp_str.as_bytes()) {
        let n =  r.read(&mut buf).await?;
        if n <= 0 {
            return static_err("CTRL网络断开");
        }
        unsafe {
            resp_str.push_str( from_utf8_unchecked( &buf[..n] ) );
        }
    }

    let list: Vec<&str> = resp_str.split("III").collect();
    if list.len() != 6 {
        return static_err("list len != 6");
    }

    if list[0] != "/f/b" {
        return static_err("head fail not /f/b");
    }

    let cnt: u16 = list[1].parse().map_app("解析cnt失败")?;
    let cmd_code: u32 = list[2].parse().map_app("解析cmd_code失败")?;
    let data_len: u32 = list[3].parse().map_app("解析len失败")?;
    let data = list[4].to_owned();

    let resp = CtrlProto {
        cnt,
        cmd_code,
        data_len,
        data
    };

    Ok(resp)
}

pub async fn read_resp_with_timeout<R: AsyncRead + Unpin>(r: &mut R) -> Result<CtrlProto, AppErr> {

    timeout(READ_DURATION,  read_resp(r))
    .await
    .map_app("读取返回超时")?
}

